#include <sw/redis++/redis++.h>
#include <mysql/mysql.h>
#include <iostream>
#include <chrono>
#include <string>
#include <vector>
#include <ctime>
#include <thread>
#include <optional>
#include <cstdlib>
#include <sstream>
#include "task.hpp"  
#include "/usr/include/boost/optional.hpp"

using namespace std;

enum class Rank {
    Bronze,
    Silver0,
    Silver1,
    Gold0,
    Gold1,
    Diamond0,
    Diamond1,
    Diamond2,
    Master,
    King
};



struct MatchRule {
    vector<Rank> allowedMatchRanks;
    int maxTotalSessionDiff;
    int maxWinSessionDiff;
};


MatchRule getMatchRule(Rank rank) {
    switch (rank) {
        case Rank::Bronze:
            return {{Rank::Bronze, Rank::Silver0}, 0, 1};
        case Rank::Silver0:
            return {{Rank::Silver0, Rank::Silver1, Rank::Gold0}, 0, 1};
        case Rank::Silver1:
            return {{Rank::Silver1, Rank::Gold0, Rank::Gold1}, 0, 1};
        case Rank::Gold0:
            return {{Rank::Gold0, Rank::Gold1, Rank::Diamond0}, 0, 1};
        case Rank::Gold1:
            return {{Rank::Gold1, Rank::Diamond0, Rank::Diamond1}, 0, 1};
        case Rank::Diamond0:
            return {{Rank::Diamond0, Rank::Diamond1, Rank::Diamond2}, 0, 1};
        case Rank::Diamond1:
            return {{Rank::Diamond1, Rank::Diamond2, Rank::Master}, 0, 1};
        case Rank::Diamond2:
            return {{Rank::Diamond2, Rank::Master, Rank::King}, 0, 1};
        case Rank::Master:
            return {{Rank::Master, Rank::King}, 0, 1};
        case Rank::King:
            return {{Rank::King}, 0, 1};
        default:
            return {{}, 0, 0};  // 无效匹配规则
    }
}

MatchRule getMatchRule2(Rank rank) {
    std::vector<Rank> allRanks = {
        Rank::Bronze, Rank::Silver0, Rank::Silver1,
        Rank::Gold0, Rank::Gold1,
        Rank::Diamond0, Rank::Diamond1, Rank::Diamond2,
        Rank::Master, Rank::King
    };

    switch (rank) {
        case Rank::Bronze:
            return {allRanks, 0, 1};
        case Rank::Silver0:
            return {allRanks, 0, 1};
        case Rank::Silver1:
            return {allRanks, 0, 1};
        case Rank::Gold0:
            return {allRanks, 0, 1};
        case Rank::Gold1:
            return {allRanks, 0, 1};
        case Rank::Diamond0:
            return {allRanks, 0, 1};
        case Rank::Diamond1:
            return {allRanks, 0, 1};
        case Rank::Diamond2:
            return {allRanks, 0, 1};
        case Rank::Master:
            return {allRanks, 0, 1};
        case Rank::King:
            return {allRanks, 0, 1};
        default:
            return {{}, 0, 0};  // 无效匹配规则
    }
}



struct BattleRecord {
    string userID;
    string uploadTime;
    Rank rank;
    int totalSession;
    int winSession;

    string uniqueKey() const {
        return userID + ":" + uploadTime;
    }

    string serialize() const {
        return userID + ":" + uploadTime + ":" +
               to_string(static_cast<int>(rank)) + ":" +
               to_string(totalSession) + ":" +
               to_string(winSession);
    }

    static BattleRecord deserialize(const string& str) {
        stringstream ss(str);
        string segment;
        vector<string> tokens;

        while (getline(ss, segment, ':')) {
            tokens.push_back(segment);
        }

        if (tokens.size() != 5) {
            throw runtime_error("Invalid BattleRecord string: " + str);
        }

        BattleRecord record;
        record.userID = tokens[0];
        record.uploadTime = tokens[1];
        record.rank = static_cast<Rank>(stoi(tokens[2]));
        record.totalSession = stoi(tokens[3]);
        record.winSession = stoi(tokens[4]);
        return record;
    }
};



void addToMatchPool(sw::redis::Redis& redis, const BattleRecord& record) {
    string key = "match_pool:" + to_string(static_cast<int>(record.rank));
    long long timestamp = chrono::duration_cast<chrono::seconds>(
        chrono::system_clock::now().time_since_epoch()).count();
    redis.zadd(key, record.serialize(), timestamp);
    cout << "添加至匹配池: " << key << " -> " << record.uniqueKey() << endl;
}

boost::optional<string> findMatchFromMySQLWithRule(const BattleRecord& self) {
 
    MYSQL* conn = DBConnectionPool::getInstance()->getConnection();
    MySQLConnectionGuard saveAndRelease(conn);
    if (!conn) {
        cerr << "获取 MySQL 连接失败" << endl;
        return {};
    }

    auto matchRule = getMatchRule(self.rank);
    vector<Rank> allowedRanks = matchRule.allowedMatchRanks;
    if(allowedRanks.size()==0)
    {
        return self.userID + ":" + self.uploadTime;
    }

    stringstream rankCondition;
    for (size_t i = 0; i < allowedRanks.size(); ++i) {
        rankCondition << "'" <<  static_cast<int>(allowedRanks[i]) << "'";
        if (i < allowedRanks.size() - 1) rankCondition << ",";
    }

    std::cout << "rankCondition.str()"<< rankCondition.str() <<std::endl;




    string query = "SELECT userID, uploadTime, totalSession, winSession FROM saveCardDatas "
               "WHERE level IN (" + rankCondition.str() + ") "
               "AND sumUsed < 3 AND sumWin < 2";


    if (mysql_query(conn, query.c_str()) != 0) {
        cerr << "MySQL 查询失败: " << mysql_error(conn) << endl;
        return {};
    }


    MYSQL_RES* result = mysql_store_result(conn);
    if (!result) {
        cerr << "获取结果失败: " << mysql_error(conn) << endl;
        return {};
    }

    std::cout<< "获取匹配结果成功"<<std::endl;

    vector<string> validCandidates;
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result))) {
        string userID = row[0];
        string uploadTime = row[1];
        int totalSession = atoi(row[2]);
        int winSession = atoi(row[3]);





        if (userID == self.userID && uploadTime == self.uploadTime) continue;
   
        auto matchRule = getMatchRule(self.rank);
       
        if (abs(totalSession - self.totalSession) <= matchRule.maxTotalSessionDiff &&
            abs(winSession - self.winSession) <= matchRule.maxWinSessionDiff) {



            validCandidates.emplace_back(userID + ":" + uploadTime);
            if (validCandidates.size() >= 20) break;
        }
    }

    mysql_free_result(result);

    


    if (!validCandidates.empty()) {
        srand(time(nullptr));  // 使用当前时间作为随机数种子
        int randomIndex = rand() % validCandidates.size();  // 随机选择一个索引
        
        return validCandidates[randomIndex];  // 返回该候选人
    }

    validCandidates.emplace_back(self.userID + ":" + self.uploadTime);
    std::cout << "validCandidates[0]: "<<validCandidates[0]<<std::endl;
    return validCandidates[0];
}



boost::optional<string> findMatchFromMySQLRAND(const BattleRecord& self) {
    MYSQL* conn = DBConnectionPool::getInstance()->getConnection();
    MySQLConnectionGuard saveAndRelease(conn);
    if (!conn) {
        cerr << "获取 MySQL 连接失败" << endl;
        return {};
    }

    auto matchRule = getMatchRule2(self.rank);
    vector<Rank> allowedRanks = matchRule.allowedMatchRanks;
    if(allowedRanks.size()==0)
    {
        return self.userID + ":" + self.uploadTime;
    }

    stringstream rankCondition;
    for (size_t i = 0; i < allowedRanks.size(); ++i) {
        rankCondition << "'" <<  static_cast<int>(allowedRanks[i]) << "'";
        if (i < allowedRanks.size() - 1) rankCondition << ",";
    }

    string query = "SELECT userID, uploadTime, totalSession, winSession FROM saveCardDatas "
               "WHERE level IN (" + rankCondition.str() + ") "
               "AND sumUsed < 3 AND sumWin < 2";


    if (mysql_query(conn, query.c_str()) != 0) {
        cerr << "MySQL 查询失败: " << mysql_error(conn) << endl;
        return {};
    }

    MYSQL_RES* result = mysql_store_result(conn);
    if (!result) {
        cerr << "获取结果失败: " << mysql_error(conn) << endl;
        return {};
    }

    std::cout<< "获取匹配结果成功"<<std::endl;

    vector<string> validCandidates;
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result))) {
        string userID = row[0];
        string uploadTime = row[1];
        int totalSession = atoi(row[2]);
        int winSession = atoi(row[3]);



        if (userID == self.userID && uploadTime == self.uploadTime) continue;

        auto matchRule = getMatchRule(self.rank);
        if (abs(totalSession - self.totalSession) <= matchRule.maxTotalSessionDiff &&
            abs(winSession - self.winSession) <= matchRule.maxWinSessionDiff) {
            validCandidates.emplace_back(userID + ":" + uploadTime);
            if (validCandidates.size() >= 20) break;
        }
    }

    mysql_free_result(result);

    


    if (!validCandidates.empty()) {
        srand(time(nullptr));  // 使用当前时间作为随机数种子
        int randomIndex = rand() % validCandidates.size();  // 随机选择一个索引
        std::cout << "validCandidates[randomIndex]: "<<validCandidates[randomIndex]<<std::endl;
        return validCandidates[randomIndex];  // 返回该候选人
    }

    validCandidates.emplace_back(self.userID + ":" + self.uploadTime);
    std::cout << "validCandidates[0]: "<<validCandidates[0]<<std::endl;
    return validCandidates[0];
}


boost::optional<string> findMatch(sw::redis::Redis& redis, const BattleRecord& self, int timeoutSeconds) {
    MatchRule rule = getMatchRule(self.rank);
    long long start = chrono::system_clock::now().time_since_epoch() / chrono::seconds(1);

    while (true) {
        for (const Rank& r : rule.allowedMatchRanks) {
            string key = "match_pool:" + to_string(static_cast<int>(r));

            vector<string> candidates;
            redis.zrange(key, 0, -1, inserter(candidates, candidates.begin()));

            vector<string> validCandidates;

            for (const auto& candidateStr : candidates) {
                BattleRecord candidate = BattleRecord::deserialize(candidateStr);
                if (candidate.userID == self.userID && candidate.uploadTime == self.uploadTime) continue;

                if (abs(candidate.totalSession - self.totalSession) <= rule.maxTotalSessionDiff &&
                    abs(candidate.winSession - self.winSession) <= rule.maxWinSessionDiff) {
                    
                    validCandidates.push_back(candidateStr);
                }
            }
            if (!validCandidates.empty()) {
                srand(time(nullptr));  
                int randomIndex = rand() % validCandidates.size();  
                string selectedCandidate = validCandidates[randomIndex];

                redis.zrem(key, selectedCandidate);  
                cout << "Redis 匹配成功: " << selectedCandidate << endl;
                return selectedCandidate;
            }
        }

        long long now = chrono::system_clock::now().time_since_epoch() / chrono::seconds(1);
        if (now - start >= timeoutSeconds) break;

        this_thread::sleep_for(chrono::seconds(1));
    }

    cout << "Redis 匹配超时，尝试从数据库获取卡组..." << endl;
    return findMatchFromMySQLWithRule(self);
}





boost::optional<string> randFindMatch(sw::redis::Redis& redis, const BattleRecord& self, int timeoutSeconds) {
    MatchRule rule = getMatchRule2(self.rank);
    long long start = chrono::system_clock::now().time_since_epoch() / chrono::seconds(1);

    while (true) {
        for (const Rank& r : rule.allowedMatchRanks) {
            string key = "match_pool:" + to_string(static_cast<int>(r));

            vector<string> candidates;
            redis.zrange(key, 0, -1, inserter(candidates, candidates.begin()));

            vector<string> validCandidates;

            for (const auto& candidateStr : candidates) {
                BattleRecord candidate = BattleRecord::deserialize(candidateStr);
                if (candidate.userID == self.userID && candidate.uploadTime == self.uploadTime) continue;

              
                if (abs(candidate.totalSession - self.totalSession) <= rule.maxTotalSessionDiff &&
                    abs(candidate.winSession - self.winSession) <= rule.maxWinSessionDiff) {
                    
                    validCandidates.push_back(candidateStr);
                }
                
            }
            if (!validCandidates.empty()) {
                srand(time(nullptr));  
                int randomIndex = rand() % validCandidates.size();  
                string selectedCandidate = validCandidates[randomIndex];

                redis.zrem(key, selectedCandidate);  
                cout << "Redis 匹配成功: " << selectedCandidate << endl;
                return selectedCandidate;
            }
        }

        long long now = chrono::system_clock::now().time_since_epoch() / chrono::seconds(1);
        if (now - start >= timeoutSeconds) break;

        this_thread::sleep_for(chrono::seconds(1));
    }

    cout << "Redis 匹配超时，尝试从数据库获取卡组..." << endl;
    return findMatchFromMySQLRAND(self);
}


boost::optional<string> findMatchFromMySQLWithExtra(const BattleRecord& self) {
    
    MYSQL* conn = DBConnectionPool::getInstance()->getConnection();
    MySQLConnectionGuard saveAndRelease(conn);
    if (!conn) {
        cerr << "获取 MySQL 连接失败" << endl;
        return {};
    }

    auto matchRule = getMatchRule(self.rank);
    vector<Rank> allowedRanks = matchRule.allowedMatchRanks;


    if(allowedRanks.size()==0)
    {
        return self.userID + ":" + self.uploadTime;
    }


    stringstream rankCondition;
    for (size_t i = 0; i < allowedRanks.size(); ++i) {
        rankCondition << "'" <<  static_cast<int>(allowedRanks[i]) << "'";
        if (i < allowedRanks.size() - 1) rankCondition << ",";
    }


    string query = "SELECT userID, uploadTime, totalSession, winSession FROM saveCardDatas "
               "WHERE level IN (" + rankCondition.str() + ") "
               "AND extra = 'NoShuffle' "
               "AND sumUsed < 3 AND sumWin < 2";


    if (mysql_query(conn, query.c_str()) != 0) {
        cerr << "MySQL 查询失败: " << mysql_error(conn) << endl;
        return {};
    }

    MYSQL_RES* result = mysql_store_result(conn);
    if (!result) {
        cerr << "获取结果失败: " << mysql_error(conn) << endl;
        return {};
    }

    std::cout<< "获取匹配结果成功"<<std::endl;

    vector<string> validCandidates;
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result))) {
        string userID = row[0];
        string uploadTime = row[1];
        int totalSession = atoi(row[2]);
        int winSession = atoi(row[3]);


        if (userID == self.userID && uploadTime == self.uploadTime) continue;

        auto matchRule = getMatchRule(self.rank);
        if (abs(totalSession - self.totalSession) <= matchRule.maxTotalSessionDiff &&
            abs(winSession - self.winSession) <= matchRule.maxWinSessionDiff) {
            validCandidates.emplace_back(userID + ":" + uploadTime);
            if (validCandidates.size() >= 20) break;
        }
    }

    mysql_free_result(result);

    if (!validCandidates.empty()) {
        srand(time(nullptr));  // 使用当前时间作为随机数种子
        int randomIndex = rand() % validCandidates.size();  // 随机选择一个索引

        std::cout << "validCandidates[randomIndex]: "<<validCandidates[randomIndex]<<std::endl;
        return validCandidates[randomIndex];  // 返回该候选人
    }

    validCandidates.emplace_back(self.userID + ":" + self.uploadTime);

    std::cout << "validCandidates[0]: "<<validCandidates[0]<<std::endl;

    return validCandidates[0];
}